package io.terminus.paas.profiler.service.impl

import com.alibaba.dubbo.common.Constants
import com.alibaba.dubbo.common.utils.StringUtils
import io.terminus.paas.profiler.AbstractProfilerService
import io.terminus.paas.profiler.model.Configuration
import io.terminus.paas.profiler.service.ConfigurationService
import io.terminus.paas.profiler.utils.ProfilerConstants
import io.terminus.paas.profiler.utils.sync.SyncUtils
import io.terminus.paas.profiler.model.ServiceModel
import io.terminus.paas.profiler.service.PermissionService
import io.terminus.paas.profiler.service.ServiceService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.net.URLDecoder
import java.net.URLEncoder
import java.util.*

/**
 * Created by gengrong on 16/3/16.
 *
 */
@Service
class ServiceServiceImpl @Autowired constructor(
        private val configurationService: ConfigurationService,
        private val permissionService: PermissionService
) : ServiceService, AbstractProfilerService() {

    override fun findByMachine(registryAddress: String, machine: String): List<ServiceModel> {
        val ret = arrayListOf<ServiceModel>()
        val serviceIdSet = hashSetOf<String>()
        getProviderUrls(registryAddress).forEach {
            for (p in it.value) {
                val ip = p.value.host + ":" + p.value.port
                if (ip.equals(machine)) {
                    serviceIdSet.add(it.key)
                    break
                }
            }
        }
        serviceIdSet.forEach { ret.add(buildServiceModel(registryAddress, it)) }
        return ret
    }

    override fun getDetailByServiceId(registryAddress: String, serviceId: String): ServiceModel {
        val service = buildServiceModel(registryAddress, serviceId)
        service.consumerNum = getConsumerUrls(registryAddress)[serviceId]?.size ?: 0
        service.providersNum = getProviderUrls(registryAddress)[serviceId]?.size ?: 0

        val providers = getProviderUrls(registryAddress)[serviceId];
        val configurators = getConfiguratorUrls(registryAddress)[serviceId]
        providers?.values?.forEach {
            service.machines.add(it.address)
            it.parameters[Constants.APPLICATION_KEY]?.let { service.applications.add(it) }
        }
        // TODO service 监控

        // 标签
        configurators?.values?.forEach {
            var tagsString = it.parameters[ProfilerConstants.PAMPAS_TAGS];
            tagsString = tagsString?.let { URLDecoder.decode(tagsString, "UTF-8") }
            tagsString?.split(",")?.let { service.tags.addAll(it) }
        }

        service.permission = permissionService.findByServiceId(registryAddress, serviceId);
        return service;
    }

    override fun updateTag(registryAddress: String, serviceId: String, tags: String?) {
        val urlMap = getConfiguratorUrls(registryAddress)[serviceId];
        if (urlMap == null) {
            tags ?: return
            val config = Configuration();
            config.service = serviceId
            config.parameters.put(ProfilerConstants.PAMPAS_TAGS, URLEncoder.encode(tags, "UTF-8"));
            configurationService.saveConfiguration(registryAddress, config);
        } else {
            urlMap.forEach { e ->
                val config = SyncUtils.url2Configuration(e)!!;
                if (tags != null) {
                    config.parameters.put(ProfilerConstants.PAMPAS_TAGS, URLEncoder.encode(tags, "UTF-8"));
                } else {
                    val params = StringUtils.parseQueryString(config.params);
                    params.remove("ProfilerConstants.PAMPAS_TAGS")
                    config.params = StringUtils.toQueryString(params);
                }
                configurationService.updateOverride(registryAddress, config);
                return
            }
        }


    }

    override fun findByTag(registryAddress: String, tag: String): List<ServiceModel> {
        val ret = arrayListOf<ServiceModel>()
        getConfiguratorUrls(registryAddress).forEach { m ->
            val url = m.value.values.firstOrNull {
                var tagsString = it.parameters[ProfilerConstants.PAMPAS_TAGS];
                tagsString = tagsString?.let { URLDecoder.decode(tagsString, "UTF-8") }
                tagsString?.split(",")?.firstOrNull { tag == it } != null
            }
            url?.let { ret.add(buildServiceModel(registryAddress, m.key)) }
        }
        return ret
    }

    fun filterByTag(registryAddress: String, tag: String): List<String> {
        val ret = arrayListOf<String>()
        getConfiguratorUrls(registryAddress).forEach { m ->
            val url = m.value.values.firstOrNull {
                var tagsString = it.parameters[ProfilerConstants.PAMPAS_TAGS];
                tagsString = tagsString?.let { URLDecoder.decode(tagsString, "UTF-8") }
                tagsString?.split(",")?.firstOrNull { it.contains(tag, true) } != null
            }
            url?.let { ret.add(m.key) }
        }
        return ret
    }

    override fun findByApplication(registryAddress: String, application: String): List<ServiceModel> {
        val ret = arrayListOf<ServiceModel>()
        val serviceIdSet = hashSetOf<String>()
        getProviderUrls(registryAddress).forEach {
            for (p in it.value) {
                val appName = p.value.parameters[Constants.APPLICATION_KEY]
                appName ?: continue
                if (appName.equals(application)) {
                    serviceIdSet.add(it.key)
                    break
                }
            }
        }
        serviceIdSet.forEach { ret.add(buildServiceModel(registryAddress, it)) }
        return ret
    }

    override fun findByQuery(registryAddress: String, query: String): List<ServiceModel> {
        val ret = arrayListOf<ServiceModel>()
        val serviceIdSet = hashSetOf<String>()
        serviceIdSet.addAll(filterByApplication(registryAddress, query))
        serviceIdSet.addAll(filterByServiceId(registryAddress, query))
        serviceIdSet.addAll(filterByMachine(registryAddress, query))
        serviceIdSet.addAll(filterByTag(registryAddress, query))
        serviceIdSet.forEach { ret.add(buildServiceModel(registryAddress, it)) }
        return ret
    }

    override fun listAll(registryAddress: String): List<ServiceModel> {
        val ret = arrayListOf<ServiceModel>()
        val serviceIdSet = hashSetOf<String>()
        serviceIdSet.addAll(getConsumerUrls(registryAddress).keys.asIterable())
        serviceIdSet.addAll(getProviderUrls(registryAddress).keys.asIterable())
        serviceIdSet.forEach { ret.add(buildServiceModel(registryAddress, it)) }
        return ret
    }

    private fun buildServiceModel(registryAddress: String, serviceId: String): ServiceModel {
        var service = ServiceModel(serviceId)
        service.status = if (getProviderUrls(registryAddress)[serviceId] == null)
            ServiceModel.STATUS.NO_PROVIDER
        else if (getConsumerUrls(registryAddress)[serviceId] == null)
            ServiceModel.STATUS.NO_CONSUMER
        else
            ServiceModel.STATUS.OK
        return service
    }

    private fun filterByApplication(registryAddress: String, query: String): HashSet<String> {
        val serviceIdSet = hashSetOf<String>()
        getProviderUrls(registryAddress).forEach {
            for (p in it.value) {
                val appName = p.value.parameters[Constants.APPLICATION_KEY]
                appName ?: continue
                if (appName.indexOf(query) > -1) {
                    serviceIdSet.add(it.key)
                    break
                }
            }
        }
        getConsumerUrls(registryAddress).forEach {
            for (p in it.value) {
                val appName = p.value.parameters[Constants.APPLICATION_KEY]
                appName ?: continue
                if (appName.indexOf(query) > -1) {
                    serviceIdSet.add(it.key)
                    break
                }
            }
        }
        return serviceIdSet
    }

    private fun filterByServiceId(registryAddress: String, query: String): HashSet<String> {
        val serviceIdSet = hashSetOf<String>()
        getProviderUrls(registryAddress).forEach {
            if (it.key.toLowerCase().indexOf(query.toLowerCase()) > -1)
                serviceIdSet.add(it.key)
        }
        getConsumerUrls(registryAddress).forEach {
            if (it.key.toLowerCase().indexOf(query.toLowerCase()) > -1)
                serviceIdSet.add(it.key)
        }
        return serviceIdSet
    }

    private fun filterByMachine(registryAddress: String, query: String): HashSet<String> {
        val serviceIdSet = hashSetOf<String>()
        getProviderUrls(registryAddress).forEach {
            for (p in it.value) {
                val ip = p.value.host + ":" + p.value.port
                if (ip.indexOf(query) > -1) {
                    serviceIdSet.add(it.key)
                    break
                }
            }
        }
        getConsumerUrls(registryAddress).forEach {
            for (p in it.value) {
                val ip = p.value.host + ":" + p.value.port
                if (ip.indexOf(query) > -1) {
                    serviceIdSet.add(it.key)
                    break
                }
            }
        }
        return serviceIdSet
    }
}